/**
 * 
 */
package edu.sjtu.se.prophet.cluster;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import org.apache.log4j.Logger;

/**
 * @author willard
 * 
 */
public class SimpleFastMedoids extends BaseMedoids {
    private static Logger logger = Logger.getLogger(SimpleFastMedoids.class);

    private int previousCost = Integer.MAX_VALUE;

    @Override
    protected Collection<Cluster> cluster() {
        selectInitialMedoids();

        int iteration = 0;

        while (true) {
            iteration++;
            logger.info("Cluster iteration " + iteration);

            logger.info("update medoids");
            updateMedoids();

            logger.info("associateDataPoints");
            associateDataPoints();

            int cost = getCost();
            if (cost == previousCost)
                break;
            else {
                System.out.println("New cost " + cost);
                logger.info("New cost " + cost);
                previousCost = cost;
            }
        }

        return clusters;
    }

    private void updateMedoids() {
        for(Point medoid : medoids) {
            Point newMedoid = findNewMedoid(medoid);
            ((MedoidCluster)medoid.getCluster()).setMedoid(newMedoid);
        }

    }

    private Point findNewMedoid(Point medoid) {
        long minDistance = Long.MAX_VALUE;
        Point minPoint = medoid;
        for (Point i : medoid.getCluster().getPoints()) {
            long distance = 0;
            for (Point j : medoid.getCluster().getPoints())
                distance += meter.distance(i, j);
            if (distance < minDistance) {
                minDistance = distance;
                minPoint = i;
            }
        }
        return minPoint;
    }

    private void selectInitialMedoids() {
        List<Pair> vectors = new ArrayList<Pair>();
        for (Point j : points) {
            double vector = 0;
            for (Point i : points) {
                int divident = 0;
                for (Point l : points) {
                    divident += meter.distance(i, l);
                }
                if (divident == 0) {
                    vector = Double.MAX_VALUE;
                    break;
                } else
                    vector += (meter.distance(i, j) / divident);
            }
            vectors.add(new Pair(vector, j));
        }

        Collections.sort(vectors);

        medoids = new HashSet<Point>();
        for (int i = 0; i < numberOfClusters; i++) {
            medoids.add(vectors.get(i).point);
        }

        clusters = new ArrayList<Cluster>();
        for (Point medoid : medoids) {
            clusters.add(new MedoidCluster(medoid));
        }

        previousCost = getCost();
    }

    public static class Pair implements Comparable<Pair> {
        public Pair(double vector, Point p) {
            this.vector = vector;
            this.point = p;
        }

        public double vector = 0;
        public Point point = null;

        @Override
        public int compareTo(Pair o) {
            if (this.vector < o.vector)
                return -1;
            else if (this.vector > o.vector)
                return 1;
            return 0;
        }
    }

}
